#include "leppbisectionthread.h"
#include <iostream>
#include <QtCore/QThread>
#include <QtDebug>

LeppBisectionThread::LeppBisectionThread(ConcurrentLeppBisection *clb, Mesh *mesh,int expectedSize, QString log_filename)
	: _log_file(log_filename)
	/*tp(expectedSize*4),
	vp(expectedSize*2)*/
{
	_log_file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
	_clb = clb;
	_mesh = mesh;

}

LeppBisectionThread::~LeppBisectionThread()
{
	_log_file.close();
}

void LeppBisectionThread::writeLog(QString message){
	QTextStream out(&_log_file);
	out << QThread::currentThread() << " " << message << "\n";
	out.flush();
}

void LeppBisectionThread::run(){
	Triangle *t = _clb->getNext();
	while(t!=0){
		LeppBisectionThread::processTriangle(t);
		/*if(LeppBisectionThread::processTriangle(t))
			writeLog(QString("run() procesado"));
		else
			writeLog(QString("run() fallo"));*/
		t = _clb->getNext();
	}
	//writeLog(QString("run() terminando"));
}

bool LeppBisectionThread::processTriangle(Triangle *t){
	QTextStream out(&_log_file);
	Q_ASSERT(t!=0);
	bool couldGet = false;
	while(true){
		//writeLog(QString("processTriangle(") + QString::number(t->getId()) + QString(")"));
		//obtengo el vecino por la arista mas larga, u
		Triangle *u = t->tryToGetNeighbor(t->longestEdgeIndex(),&couldGet,out);
		if(!couldGet){
			//writeLog(QString("processTriangle(") + QString::number(t->getId()) + QString(") falle al tomar al vecino"));
			break;
		}
		//si es nulo estoy en el borde, corto nomas
		if(u==0){
			//writeLog(QString("processTriangle(") + QString::number(t->getId()) + QString(") estoy en el borde, corto"));
			//antes de refinar debo intentar tomar todos los vecinos para evitar condiciones de borde raras
			refineTriangle(t,0);
			/*if(trylockNeighbors(t,0)){
				refineTriangle(t,0);
			} else {
				couldGet = false;
			}*/
			break;
		} else {
			//si comparten la arista mas larga entonces corto
			Edge e_u = u->getEdge(u->longestEdgeIndex());
			Edge e_t = t->getEdge(t->longestEdgeIndex());
			if(e_u.sameVertexsId(&e_t))
			{
				//writeLog(QString("processTriangle(") + QString::number(t->getId()) + QString(") comparto arista mas larga con U=") +QString::number(u->getId()));
				//antes de refinar debo intentar tomar todos los vecinos para evitar condiciones de borde raras
				refineTriangle(t,u);
				/*if(trylockNeighbors(t,u)){
					refineTriangle(t,u);
				} else {
					couldGet = false;
					u->getMutex()->unlock();
				}*/
				break;
			}
			else
			{
				//writeLog(QString("processTriangle(") + QString::number(t->getId()) + QString(") no comparto arista asi que continue U=") +QString::number(u->getId()));
				if(!LeppBisectionThread::processTriangle(u)){
					//si devuelve false significa que uno de mis "hijos" no pudo conseguir
					//el lock asi que yo tambien fallo
					couldGet = false;
					break;
				}
			}
		}
	}
	if(couldGet){
		//writeLog(QString("processTriangle(...) terminando bien"));
		return true;
	} else {
		//writeLog(QString("processTriangle(...) terminando fallido"));
		t->getMutex()->unlock();
		return false;
	}
}

bool LeppBisectionThread::trylockNeighbors(Triangle *t,Triangle *u){
	//inicializacion
	bool couldLock = true;
	Triangle* originals[2];
	originals[0] = t;
	originals[1] = u;
	int n = 2;
	if(u==0)
		n = 1;
	Triangle* lockedNeighbors[n][2];
	for(int i=0;i<n;i++)
		for(int j=0;j<2;j++)
			lockedNeighbors[i][j] = 0;

	//trato de tomar los vecinos
	for(int i=0;i<n;i++){
		int k=0;
		for(int j=0;j<3;j++){
			Triangle* neighbor = originals[i]->getNeighbor(j);
			if(neighbor != originals[i+1]){
				if(neighbor->getMutex()->tryLock()){
					lockedNeighbors[i][k] = neighbor;
				} else {
					//el triangulo t debiera tener un mutex pedido (por donde viene el
					//lepp) asi que le pido uno menos
					//TODO: arreglar esta cosa, no lo arreglo qp no lo ocupare
					couldLock = false;
				}
				k++;
			}
		}
		Q_ASSERT(k==2); //debiera tener solo 2 vecinos
	}
	//si fallo entonces suelto los vecinos
	if(!couldLock)
		for(int i=0;i<n;i++)
			for(int j=0;j<2;j++)
				if(lockedNeighbors[i][j] != 0)
					lockedNeighbors[i][j]->getMutex()->unlock();

	return couldLock;
}

Triangle * LeppBisectionThread::getNeighbor(Triangle *t){
	//return t->getLockedNeighbor(t->longestEdgeIndex());
	std::cout << "estoy ocupando un metodo no adecuado!" << std::endl;
	return 0;
}

Vertex * LeppBisectionThread::fetchNewVertex(int id, Point2F p){
	//return vp.fetchVertex(id,p);
	return new Vertex(id,p);
}

Triangle * LeppBisectionThread::fetchNewTriangle(int id,Vertex *v1, Vertex *v2, Vertex *v3){
	//return tp.fetchTriangle(id,v1,v2,v3);
	return new Triangle(id,v1,v2,v3);
}

void LeppBisectionThread::addVertexToMesh(Vertex *v){
	_mesh->addVertex(v);
}

void LeppBisectionThread::addTriangleToMesh(Triangle *t){
	_mesh->addTriangle(t);
}

void LeppBisectionThread::finalizeTriangle(Triangle *t){
	//writeLog(QString("finalizeTriangle(") + QString::number(t->getId()) + QString(") 1"));
	_clb->removeFromPending(t);
	//writeLog(QString("finalizeTriangle(") + QString::number(t->getId()) + QString(") 2"));
	_mesh->destroyTriangle(t);
	//writeLog(QString("finalizeTriangle() 3"));
}

void LeppBisectionThread::refineTriangle(Triangle *t, Triangle *u){
	/*QString fn_name(
			"LeppBisectionThread::refineTriangle("+
			QString::number(t->getId()) +
			QString(",") +
			((u==0)?QString("null"):QString::number(u->getId())) +
			")");
	writeLog(fn_name);*/

	Triangle* originals[2];
	originals[T] = t;
	originals[U] = u;

	//organizo los vertices de los triangulos
	Vertex* vertexes[2][3];
	int aux_t_longest_edge = t->longestEdgeIndex();
	vertexes[T][SHARED] = t->getVertex(aux_t_longest_edge);
	vertexes[T][FIRST_NON_SHARED] = t->getVertex((aux_t_longest_edge+1)%3);
	vertexes[T][SECOND_NON_SHARED] = t->getVertex((aux_t_longest_edge+2)%3);
	if(u!=0){
		int aux_u_longest_edge = u->longestEdgeIndex();
		vertexes[U][SHARED] = u->getVertex(aux_u_longest_edge);
		vertexes[U][FIRST_NON_SHARED] = vertexes[T][FIRST_NON_SHARED];
		vertexes[U][SECOND_NON_SHARED] = vertexes[T][SECOND_NON_SHARED];
	}

	//pruebas
	//Q_ASSERT(vertexTest(vertexes,u));

	//creo nuevo vertice
	Edge e_t = t->getEdge(t->longestEdgeIndex());
	Vertex *new_vertex = fetchNewVertex(-1,e_t.midPoint());
	addVertexToMesh(new_vertex);

	//creo los nuevos triangulos
	Triangle* new_triangles[2][2];
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			if(i==U&&u==0){
				new_triangles[i][j] = 0;
			} else {
				new_triangles[i][j] = fetchNewTriangle(
						-1,
						vertexes[i][SHARED],
						new_vertex,
						vertexes[i][j]);
				addTriangleToMesh(new_triangles[i][j]);
			}
		}
	}
	//writeLog(QString("arreglando vecinos"));
	//arreglo los vecinos
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			if(!(i==U&&u==0)){
				//defino sus vecinos
				//writeLog(QString("arreglando vecinos 1"));
				Triangle *new_neighbor = originals[i]->getNeighborOposedToVertex(vertexes[i][(j+1)%2]);
				//writeLog(QString("arreglando vecinos 2"));
				/*writeLog(QString("new_neighbor=") +
						((new_neighbor==0)?QString("null"):QString::number(new_neighbor->getId())));*/
				new_triangles[i][j]->setNeighbors(
						new_triangles[(i+1)%2][j],
						new_neighbor,
						new_triangles[i][(j+1)%2]);
				//writeLog(QString("arreglando vecinos 3"));
				//arreglo el vecino del nuevo triangulo
				if(new_neighbor!=0)
					new_neighbor->replaceNeighbor(originals[i],new_triangles[i][j]);
				//writeLog(QString("arreglando vecinos 4"));
			}
		}
	}
	//writeLog(QString("finalizando"));
	finalizeTriangle(t);
	if(u!=0){
		finalizeTriangle(u);
	}
}
